﻿using Android.Content;
using Android.Provider;
using Android.Util;
using AndroidX.Core.Provider;
using AndroidX.DocumentFile.Provider;
using Java.Nio.FileNio.Attributes;
using Java.Util.Concurrent;
using Java.Util.Concurrent.Atomic;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Un4seen.Bass;

namespace NightPlayer
{
    /*
     
    @ChatGPT 生成一段言辞激烈的吐槽，吐槽以下内容：
    谷歌在安卓系统中添加了SAF框架，意图取代原生文件访问，导致使用Xamarin.Android要读取外部SD卡的时候，会造成以下麻烦：
    1. 无法直接使用C#原生的文件操作，需要写巨量的代码进行适配
    2. SAF框架使用了URI对路径进行编码，在使用的时候需要手动编码和解码
    3. 无法设置文件的LastModified，导致一些文件同步机制失效
    4. 若创建一个已经创建的文件时，系统会自作主张地给文件名加1
    
    */
    /// <summary>
    /// 提供了对SAF框架和传统文件的抽象访问层
    /// </summary>
    public static class AndroidIO
    {
        private static IFileIOImpl impl;

        public static IFileIOImpl GetImpl()
        {
            return impl;
        }

        public static void SetImpl(IFileIOImpl impl)
        {
            AndroidIO.impl = impl;
        }

        public static class BassStreamHelper
        {
            private static object locker = new object();
            private static nint currentPtr = 0;
            public static nint NextBassStreamUserPtr
            {
                get
                {
                    lock (locker)
                    {
                        if (currentPtr > nint.MaxValue - 10)
                        {
                            currentPtr = 0;
                        }
                        return ++currentPtr;
                    }
                }
            }

            private static Dictionary<nint, BassStreamImpl> livingInstances = new Dictionary<nint, BassStreamImpl>();

            public static BASS_FILEPROCS OpenBassStream(Stream stream, nint user)
            {
                BassStreamImpl impl = new BassStreamImpl(stream, user);
                if (livingInstances.ContainsKey(user))
                {
                    livingInstances[user] = impl;
                }
                else
                {
                    livingInstances.Add(user, impl);
                }

                return impl.GetProcs();
            }

            private class BassStreamImpl
            {
                private Stream stream;
                private nint user;

                public BassStreamImpl(Stream stream, nint user)
                {
                    this.stream = stream;
                    this.user = user;
                    dFileCloseProc = this.FileCloseProc;
                    dFileLenProc = this.FileLenProc;
                    dFileReadProc = this.FileReadProc;
                    dFileSeekProc = this.FileSeekProc;
                    dFileProcs = new BASS_FILEPROCS(dFileCloseProc, dFileLenProc, dFileReadProc, dFileSeekProc);
                }

                private FILECLOSEPROC dFileCloseProc;
                private FILELENPROC dFileLenProc;
                private FILEREADPROC dFileReadProc;
                private FILESEEKPROC dFileSeekProc;
                private BASS_FILEPROCS dFileProcs;
                public BASS_FILEPROCS GetProcs()
                {
                    return dFileProcs;
                }

                public long FileLenProc(nint user)
                {
                    return stream.Length;
                }

                public void FileCloseProc(nint user)
                {
                    stream.Close();
                    livingInstances.Remove(user);
                }

                public int FileReadProc(nint bufferPtr, int len, nint user)
                {
                    byte[] buf = new byte[len];
                    int readedlen = stream.Read(buf, 0, len);
                    if (readedlen < 0)
                    {
                        readedlen = 0;
                    }
                    Marshal.Copy(buf, 0, bufferPtr, readedlen);
                    return readedlen;
                }

                public bool FileSeekProc(long pos, nint user)
                {
                    if (stream.CanSeek)
                    {
                        stream.Seek(pos, SeekOrigin.Begin);
                        return true;
                    }
                    return false;
                }
            }
        }




        public static class Path
        {



            public static char DirectorySeparatorChar { get { return impl.PathGetDirectorySeparatorChar(); } }

            public static string GetFileNameWithoutExtension(string path)
            {
                return impl.PathGetFileNameWithoutExtension(path);
            }

            public static string GetFullPath(string musicRoot)
            {
                return impl.PathGetFullPath(musicRoot);
            }

            public static string GetExtension(string filename)
            {
                return impl.PathGetExtension(filename);
            }

            public static string GetFileName(string filename)
            {
                return impl.PathGetFileName(filename);
            }

            public static string Combine(params string[] parts)
            {
                return impl.PathCombine(parts);
            }
        }

        public static class File
        {
            public static BASS_FILEPROCS OpenBass(string path, nint user)
            {
                return BassStreamHelper.OpenBassStream(impl.FileOpenRead(path), user);
            }

            public static Stream OpenRead(string file)
            {
                return impl.FileOpenRead(file);
            }

            public static byte[] ReadAllBytes(string firSource)
            {
                return impl.FileReadAllBytes(firSource);
            }

            public static string[] ReadAllLines(string fxfile)
            {
                return impl.FileReadAllLines(fxfile);
            }

            public static void Create(string v)
            {
                impl.FileCreate(v);
            }

            public static bool Exists(string v)
            {
                return impl.FileExists(v);
            }

            public static void WriteAllLines(string fileName, IEnumerable<string> data)
            {
                impl.FileWriteAllLines(fileName, data);
            }

            public static DateTime GetLastWriteTime(string fileName)
            {
                return impl.FileGetLastWriteTime(fileName);
            }
        }

        public static class Directory
        {
            public static void CreateDirectory(string dir)
            {
                impl.DirectoryCreateDirectory(dir);
            }

            public static IEnumerable<string> EnumerateDirectories(string root)
            {
                return impl.DirectoryEnumerateDirectories(root);
            }

            public static IEnumerable<string> EnumerateFiles(string root, string filter)
            {
                return impl.DirectoryEnumerateFiles(root, filter);
            }

            public static IEnumerable<string> EnumerateFiles(string root)
            {
                return impl.DirectoryEnumerateFiles(root);
            }

            public static bool Exists(string dir)
            {
                return impl.DirectoryExists(dir);
            }

        }

        public interface IFileIOImpl
        {
            char PathGetDirectorySeparatorChar();

            string PathGetFileNameWithoutExtension(string path);

            string PathGetFullPath(string musicRoot);

            string PathGetExtension(string filename);

            string PathGetFileName(string filename);

            string PathCombine(params string[] parts);


            Stream FileOpenRead(string file);

            byte[] FileReadAllBytes(string firSource);

            string[] FileReadAllLines(string fxfile);

            void FileCreate(string v);

            bool FileExists(string v);

            void FileWriteAllLines(string fileName, IEnumerable<string> data);

            DateTime FileGetLastWriteTime(string fileName);

            void DirectoryCreateDirectory(string dir);

            IEnumerable<string> DirectoryEnumerateDirectories(string root);

            IEnumerable<string> DirectoryEnumerateFiles(string root, string filter);

            IEnumerable<string> DirectoryEnumerateFiles(string root);

            bool DirectoryExists(string dir);

        }
    }

    public class LegacyFileIOImpl : AndroidIO.IFileIOImpl
    {
        public char PathGetDirectorySeparatorChar()
        {
            return Path.DirectorySeparatorChar;
        }

        public string PathGetFileNameWithoutExtension(string path)
        {
            return Path.GetFileNameWithoutExtension(path);
        }

        public string PathGetFullPath(string musicRoot)
        {
            return Path.GetFullPath(musicRoot);
        }

        public string PathGetExtension(string filename)
        {
            return Path.GetExtension(filename);
        }

        public string PathGetFileName(string filename)
        {
            return Path.GetFileName(filename);
        }

        public string PathCombine(params string[] parts)
        {
            return Path.Combine(parts);
        }


        public Stream FileOpenRead(string file)
        {
            return File.OpenRead(file);
        }

        public byte[] FileReadAllBytes(string firSource)
        {
            return File.ReadAllBytes(firSource);
        }

        public string[] FileReadAllLines(string fxfile)
        {
            return File.ReadAllLines(fxfile);
        }

        public void FileCreate(string v)
        {
            File.Create(v);
        }

        public bool FileExists(string v)
        {
            return File.Exists(v);
        }

        public void FileWriteAllLines(string fileName, IEnumerable<string> data)
        {
            File.WriteAllLines(fileName, data);
        }

        public DateTime FileGetLastWriteTime(string fileName)
        {
            return File.GetLastWriteTime(fileName);
        }

        public void DirectoryCreateDirectory(string dir)
        {
            Directory.CreateDirectory(dir);
        }

        public IEnumerable<string> DirectoryEnumerateDirectories(string root)
        {
            return Directory.EnumerateDirectories(root);
        }

        public IEnumerable<string> DirectoryEnumerateFiles(string root, string filter)
        {
            return Directory.EnumerateFiles(root, filter);
        }

        public IEnumerable<string> DirectoryEnumerateFiles(string root)
        {
            return Directory.EnumerateFiles(root);
        }

        public bool DirectoryExists(string dir)
        {
            return Directory.Exists(dir);
        }

    }

    public class TheFxxkingSAFFileIOImpl : AndroidIO.IFileIOImpl
    {
        private string documentRootStr;
        private LegacyFileIOImpl legacyImpl;
        private DocumentFile rootDocument;
        private Context ctx;
        private string androidDataDirRoot;
        public TheFxxkingSAFFileIOImpl(Context applicationContext, string documentRootStr)
        {
            legacyImpl = new LegacyFileIOImpl();
            this.ctx = applicationContext;
            this.androidDataDirRoot = applicationContext.FilesDir.ParentFile.AbsolutePath.TrimEnd('/');
            this.documentRootStr = documentRootStr;
            rootDocument = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(documentRootStr)!)!;
            this.documentRootStr = rootDocument.Uri.ToString();
        }

        private ConcurrentDictionary<string, List<SAFFileStruct>> cachedDirectoryLists = new ConcurrentDictionary<string, List<SAFFileStruct>>();

        public class SAFFileStruct
        {
            public string MntName;
            public bool IsDir;
        }

        long statisticedLastWriteTime;
        object updateStatisticTimeSyncObj = new object();

        public long BuildCache()
        {
            statisticedLastWriteTime = 0;
            foreach (var item in DirectoryEnumerateDirectories(PathGetRoot()))
            {
                dirsNeedsToBeConcurrentlyScanned.Enqueue(item);
            };
            do
            {
                List<Thread> threads = new List<Thread>();
                var threadCount = Math.Max(2, Environment.ProcessorCount * 2);
                for (int i = 0; i < threadCount; i++)
                {
                    Thread t = new Thread(CacheWorker);
                    threads.Add(t);
                    t.Start();
                }
                threads.ForEach(t => t.Join());
                Console.WriteLine("Cache Round End, " + dirsNeedsToBeConcurrentlyScanned.Count);
            } while (dirsNeedsToBeConcurrentlyScanned.Count > 0);
            return statisticedLastWriteTime;
        }


        private ConcurrentQueue<string> dirsNeedsToBeConcurrentlyScanned = new ConcurrentQueue<string>();
        public void CacheWorker()
        {
            int c = 2;
            while (c > 0)
            {

                if (dirsNeedsToBeConcurrentlyScanned.TryDequeue(out string it))
                {

                    c = 2;
                    foreach (var item in DirectoryEnumerateDirectories(it))
                    {
                        dirsNeedsToBeConcurrentlyScanned.Enqueue(item);
                    };
                }
                else
                {
                    c--;
                    Thread.Sleep(50);
                }
            }
        }

        public void ClearCache()
        {
            cachedDirectoryLists.Clear();
        }

        private string saf2mnt(string treeuri)
        {
            if (!treeuri.StartsWith(documentRootStr))
            {
                throw new ArgumentException("The Given " + nameof(treeuri) + " is not inside the document root");
            }
            var trimmedTreeUri = treeuri.Substring(documentRootStr.Length);
            var decodedUri = Android.Net.Uri.Decode(trimmedTreeUri);
            if (!decodedUri.StartsWith("/"))
            {
                decodedUri += "/";
            }
            return "/mnt/saf" + decodedUri;
        }

        private DocumentFile mnt2file(string mntpath)
        {
            return saf2file(mnt2saf(mntpath));
        }

        private DocumentFile saf2file(string safpath)
        {
            return DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(safpath)!)!;
        }

        private string mnt2saf(string mntpath)
        {
            if (!mntpath.StartsWith("/mnt/saf"))
            {
                throw new ArgumentException("The Given " + nameof(mntpath) + " is not inside the virtual root");
            }
            var notEncodedUri = mntpath.Substring("/mnt/saf".Length);
            if (notEncodedUri.StartsWith("/"))
            {
                notEncodedUri = notEncodedUri.Substring(1);
            }
            var realRoot = documentRootStr;
            if (!realRoot.EndsWith("%2F") && !realRoot.EndsWith("%2f"))
            {
                realRoot += "%2F";
            }
            realRoot += Android.Net.Uri.Encode(notEncodedUri);
            if (realRoot.EndsWith("%2F") || realRoot.EndsWith("%2f"))
            {
                realRoot = realRoot.Substring(0, realRoot.Length - 3);
            }
            return realRoot;
        }



        public IEnumerable<string> DirectoryEnumerateDirectories(string dir)
        {
            if (PathIsNative(dir)) { return legacyImpl.DirectoryEnumerateDirectories(dir); }
            if (!DirectoryExists(dir)) { return new string[0]; }
            var df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(mnt2saf(dir))!)!;
            var files = FastGetAllFileNamesInDir(dir, df);
            return files.Where(f => f.IsDir).Select(f => f.MntName);
        }

        public IEnumerable<string> DirectoryEnumerateFiles(string dir, string filter)
        {
            if (PathIsNative(dir)) { return legacyImpl.DirectoryEnumerateFiles(dir, filter); }
            if (!DirectoryExists(dir)) { return new string[0]; }
            var df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(mnt2saf(dir))!)!;
            var files = FastGetAllFileNamesInDir(dir, df);

            Func<string, bool> filterFunc = (s) => true;
            if (filter != "*")
            {
                if (filter.StartsWith("*"))
                {
                    var kw = filter.Substring(1);
                    filterFunc = (s) => s.EndsWith(kw);
                }
                if (filter.EndsWith("*"))
                {
                    var kw = filter.Substring(0, filter.Length - 1);
                    filterFunc = (s) => s.StartsWith(kw);
                }
            }

            return files.Where(f => !f.IsDir).Where(f => filterFunc(PathGetFileName(f.MntName))).Select(f => f.MntName);
        }

        private IEnumerable<SAFFileStruct> FastGetAllFileNamesInDir(string root, DocumentFile df)
        {
            if (cachedDirectoryLists.ContainsKey(root))
            {
                return cachedDirectoryLists[root];
            }
            ContentResolver resolver = ctx.ContentResolver;
            Android.Net.Uri childrenUri = DocumentsContractCompat.BuildChildDocumentsUriUsingTree(df.Uri, DocumentsContractCompat.GetDocumentId(df.Uri));
            Android.Database.ICursor c = null;
            List<SAFFileStruct> fileNames = new List<SAFFileStruct>();
            long latestFileTime = 0;
            try
            {
                c = resolver.Query(childrenUri, new string[]
                {
                    DocumentsContract.Document.ColumnMimeType, DocumentsContract.Document.ColumnDisplayName,DocumentsContract.Document.ColumnLastModified
                }, null, null, null);
                while (c.MoveToNext())
                {
                    string fileName = c.GetString(1);
                    string fileType = c.GetString(0);
                    long lastModified = c.GetLong(2);
                    latestFileTime = Math.Max(latestFileTime, lastModified);
                    bool isDir = DocumentsContract.Document.MimeTypeDir == fileType;
                    fileNames.Add(new SAFFileStruct()
                    {
                        MntName = PathCombine(root, fileName),
                        IsDir = isDir
                    });
                }
            }
            catch (Exception ex)
            {

            }
            try
            {
                c.Close();
            }
            catch { }
            lock (updateStatisticTimeSyncObj)
            {
                statisticedLastWriteTime = Math.Max(statisticedLastWriteTime,latestFileTime);
            }
            cachedDirectoryLists.TryAdd(root, fileNames);
            return fileNames;
        }

        public IEnumerable<string> DirectoryEnumerateFiles(string dir)
        {
            return DirectoryEnumerateFiles(dir, "*");
        }
        public void DirectoryCreateDirectory(string dir)
        {
            if (PathIsNative(dir)) { legacyImpl.DirectoryCreateDirectory(dir); return; }
            Stack<string> parts = new Stack<string>();
            string currentDirTracer = dir;
            while (currentDirTracer != null && !PathIsRoot(currentDirTracer))
            {
                parts.Push(PathGetCurrent(currentDirTracer));
                currentDirTracer = PathGetParent(currentDirTracer);
            }
            var currentDir = PathGetRoot();
            while (parts.TryPop(out string? part))
            {
                var safpath = mnt2saf(currentDir);
                DocumentFile? df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(safpath)!);
                if (df == null)
                {
                    Log.Wtf("SAFAdapter", "Get saf " + safpath + " returns null");
                    throw new Exception("SAFAdapter Get saf " + safpath + " returns null");
                }
                var subdir = mnt2saf(PathCombine(currentDir, part));
                DocumentFile? subdf = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(subdir)!);
                if (subdf == null || !subdf.Exists())
                {
                    df.CreateDirectory(part);
                }
                currentDir = PathCombine(currentDir, part);
            }
        }
        public bool DirectoryExists(string dir)
        {
            if (PathIsNative(dir)) { return legacyImpl.DirectoryExists(dir); }
            // use in short form
            if (true)
            {
                var df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(mnt2saf(dir))!);
                return df != null && df.Exists() && df.IsDirectory;
            }
        }

        public void FileCreate(string v)
        {
            if (PathIsNative(v)) { legacyImpl.FileCreate(v); return; }
            Stack<string> parts = new Stack<string>();
            string currentDirTracer = v;
            while (currentDirTracer != null && !PathIsRoot(currentDirTracer))
            {
                parts.Push(PathGetCurrent(currentDirTracer));
                currentDirTracer = PathGetParent(currentDirTracer);
            }
            var currentDir = PathGetRoot();
            while (parts.TryPop(out string? part))
            {
                var safpath = mnt2saf(currentDir);
                DocumentFile? df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(safpath)!);
                if (df == null)
                {
                    Log.Wtf("SAFAdapter", "Get saf " + safpath + " returns null");
                    throw new Exception("SAFAdapter Get saf " + safpath + " returns null");
                }
                var subdir = mnt2saf(PathCombine(currentDir, part));
                DocumentFile? subdf = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(subdir)!);
                if (subdf == null || !subdf.Exists())
                {
                    if (parts.Count == 0)
                    {

                        df.CreateFile("application/octet-stream", part);
                    }
                    else
                    {

                        df.CreateDirectory(part);
                    }
                }
                else
                {
                    if (parts.Count == 0)
                    {
                        subdf.Delete();
                        df.CreateFile("application/octet-stream", part);
                    }
                }

                currentDir = PathCombine(currentDir, part);
            }
        }

        public bool FileExists(string v)
        {
            if (PathIsNative(v)) { return legacyImpl.FileExists(v); }
            if (true)
            {
                var df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(mnt2saf(v))!);
                return df != null && df.Exists() && df.IsFile;
            }
        }

        public void FileDelete(string v)
        {
            if (PathIsNative(v))
            {
                System.IO.File.Delete(v);
            }
            if (FileExists(v))
            {
                DocumentFile? df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(mnt2saf(v))!)!;
                df?.Delete();
            }
        }

        public DateTime FileGetLastWriteTime(string fileName)
        {
            if (PathIsNative(fileName))
            {
                return legacyImpl.FileGetLastWriteTime(fileName);
            }

            if (FileExists(fileName))
            {
                DocumentFile df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(mnt2saf(fileName))!)!;
                Java.Util.Date dt = new Java.Util.Date(df.LastModified());
                Java.Text.SimpleDateFormat sdf = new Java.Text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                var dateStr = sdf.Format(dt);
                return System.DateTime.ParseExact(dateStr, "yyyy\\-MM\\-dd HH\\:mm\\:ss", null);
            }
            else
            {
                Program.RequestClearCache();
                throw new FileNotFoundException(fileName);
            }
        }

        public Stream FileOpenRead(string file)
        {
            if (PathIsNative(file))
            {
                return legacyImpl.FileOpenRead(file);
            }
            if (!FileExists(file))
            {
                Program.RequestClearCache();
                throw new FileNotFoundException(file);
            }
            DocumentFile? df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(mnt2saf(file))!)!;
            return ctx.ContentResolver.OpenInputStream(df.Uri);
        }

        public byte[] FileReadAllBytes(string file)
        {

            using (MemoryStream ms = new MemoryStream())
            {
                using (var src = FileOpenRead(file))
                {
                    src.CopyTo(ms);
                }
                return ms.ToArray();
            }

        }

        public string[] FileReadAllLines(string file)
        {
            List<string> strs = new List<string>();
            using (var src = FileOpenRead(file))
            {
                StreamReader sr = new StreamReader(src);
                string? line;
                while ((line = sr.ReadLine()) != null)
                {
                    strs.Add(line!);
                }
            }
            return strs.ToArray();
        }

        public void FileWriteAllLines(string file, IEnumerable<string> data)
        {
            if (PathIsNative(file))
            {
                legacyImpl.FileWriteAllLines(file, data);
                return;
            }
            if (FileExists(file))
            {
                FileDelete(file);
            }
            FileCreate(file);
            if (data == null)
            {
                return;
            }
            DocumentFile? df = DocumentFile.FromTreeUri(ctx, Android.Net.Uri.Parse(mnt2saf(file))!)!;
            var outstream = ctx.ContentResolver.OpenOutputStream(df.Uri);
            using (outstream)
            {
                StreamWriter sw = new StreamWriter(outstream);
                foreach (var item in data)
                {
                    sw.WriteLine(item);
                }
                sw.Flush();
                outstream.Flush();
            }
        }



        public string PathCombine(params string[] parts)
        {
            var dirchar = PathGetDirectorySeparatorChar();
            return dirchar + String.Join(dirchar, parts.Select(p => p.Trim(dirchar)).ToArray());
        }

        public char PathGetDirectorySeparatorChar()
        {
            return '/';
        }

        public string PathGetExtension(string filename)
        {
            var fileName = PathGetFileName(filename);
            int lastSlashIndex = fileName.LastIndexOf('.');

            // 如果没有找到斜杠，或者斜杠在字符串的末尾，则返回整个字符串
            if (lastSlashIndex < 0)
            {
                return "";
            }
            if (lastSlashIndex == fileName.Length - 1)
            {
                return "";
            }
            // 返回最后一个斜杠后面的部分，即文件名
            return fileName.Substring(lastSlashIndex);
        }

        public string PathGetFileName(string filename)
        {
            // 找到最后一个斜杠的位置
            int lastSlashIndex = filename.LastIndexOf('/');

            // 如果没有找到斜杠，或者斜杠在字符串的末尾，则返回整个字符串
            if (lastSlashIndex < 0)
            {
                return filename;
            }
            if (lastSlashIndex == filename.Length - 1)
            {
                return "";
            }
            // 返回最后一个斜杠后面的部分，即文件名
            return filename.Substring(lastSlashIndex + 1);
        }

        public string PathGetFileNameWithoutExtension(string path)
        {
            var fileName = PathGetFileName(path);
            int lastSlashIndex = fileName.LastIndexOf('.');

            // 如果没有找到斜杠，或者斜杠在字符串的末尾，则返回整个字符串
            if (lastSlashIndex < 0)
            {
                return fileName;
            }
            if (lastSlashIndex == fileName.Length - 1)
            {
                return fileName.Substring(0, fileName.Length - 1);
            }
            // 返回最后一个斜杠后面的部分，即文件名
            return fileName.Substring(0, lastSlashIndex);
        }

        public string PathGetRoot()
        {
            return "/mnt/saf";
        }

        public bool PathIsRoot(string vpath)
        {
            if (vpath == "/mnt/saf/" || vpath == "/mnt/saf")
            {
                return true;
            }
            return false;
        }

        public bool PathIsInRoot(string vpath) => vpath.StartsWith(PathGetRoot());
        public bool PathIsNative(string vpath) => vpath.StartsWith(androidDataDirRoot);

        public string PathGetParent(string dir)
        {
            // 确保目录不是根目录
            if (PathIsRoot(dir))
            {
                return null; // 或者返回根目录，取决于你的需求
            }

            // 移除末尾的斜杠（如果有的话）
            dir = dir.TrimEnd('/');

            // 找到最后一个斜杠的位置
            int lastSlashIndex = dir.LastIndexOf('/');

            // 如果没有找到斜杠，或者斜杠是第一个字符，则返回null或者根目录
            if (lastSlashIndex <= 0)
            {
                return null; // 或者返回根目录，取决于你的需求
            }

            // 截取字符串直到最后一个斜杠的位置
            string parentDir = dir.Substring(0, lastSlashIndex);

            return parentDir;
        }
        public string PathGetCurrent(string dir)
        {
            dir = dir.TrimEnd(PathGetDirectorySeparatorChar());
            // 找到最后一个斜杠的位置
            int lastSlashIndex = dir.LastIndexOf(PathGetDirectorySeparatorChar());

            // 如果没有找到斜杠，或者斜杠在字符串的末尾，则返回整个字符串
            if (lastSlashIndex < 0)
            {
                return dir;
            }
            // 返回最后一个斜杠后面的部分，即文件名
            return dir.Substring(lastSlashIndex + 1);
        }
        public string PathGetFullPath(string musicRoot)
        {
            if (PathIsInRoot(musicRoot))
            {
                return musicRoot;
            }

            if (PathIsNative(musicRoot))
            {
                return musicRoot;
            }

            return PathCombine(PathGetRoot(), musicRoot);
        }

       
    }
}
